/*
 *
 *  * Copyright (c) 2016. For Intelligent Group
 *
 */

package com.intelligent.ispc.chart.spc;

import com.google.common.collect.Lists;
import com.intelligent.ispc.common.utils.MathUtils;
import com.intelligent.ispc.foundation.chart.AbstractChart;
import com.intelligent.ispc.foundation.chart.axis.FixedNumberAxis;
import com.intelligent.ispc.foundation.chart.util.ChartColor;
import com.intelligent.ispc.foundation.chart.util.ChartUtil;
import com.intelligent.ispc.r.dto.ScatterDto;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.Range;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleInsets;

import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List;

/**
 * Created by Can.Guan on 2016/9/9.
 * Modified by QiangChen on 2016/11/7.
 */
public class ScatterChart extends AbstractChart {

    private IntervalXYDataset dataset;
    private JFreeChart localChart;
    private String chartName;
    private ScatterDto scatterDto;
    private double[] meanData;
    private boolean isDataValid;

    /**
     * @param scatterDto is the original data of the drawing.
     */
    public ScatterChart(ScatterDto scatterDto) {
        isDataValid = true;
        this.scatterDto = scatterDto;
        this.meanData = scatterDto.getStatisticsDto().getClValue();
        chartName = "Scatter Chart";
        dataset = createDataset(this.scatterDto.getPointDto().getX(), this.scatterDto.getPointDto().getY(), this.scatterDto.getFoldLineDto().getX(), this.scatterDto.getFoldLineDto().getY());
    }

    /**
     * @param scatterDto is the original data of the drawing.
     * @param chartName  is the name of the ScatterChart.
     */
    public ScatterChart(ScatterDto scatterDto, String chartName) {
        isDataValid = true;
        this.scatterDto = scatterDto;
        this.meanData = scatterDto.getStatisticsDto().getClValue();
        this.chartName = chartName;
        dataset = createDataset(this.scatterDto.getPointDto().getX(), this.scatterDto.getPointDto().getY(), this.scatterDto.getFoldLineDto().getX(), this.scatterDto.getFoldLineDto().getY());
    }

    @Override
    protected JFreeChart buildChart() {
        //Blank panel is returned when drawing data is invalid.
        if (!isDataValid) {
            return this.blankChart(chartName);
        }

        localChart = ChartFactory.createXYLineChart(chartName, "", "", dataset, PlotOrientation.VERTICAL, false, true, false);

        XYPlot xyPlot = localChart.getXYPlot();
        xyPlot.setDomainPannable(true);
        xyPlot.setRangePannable(true);

        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesLinesVisible(0, true);
        renderer.setSeriesShapesVisible(0, true);
        renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
        final int defaultEntityRadius = 6;
        renderer.setDefaultEntityRadius(defaultEntityRadius);
        renderer.setSeriesPaint(0, ChartColor.RED);
        renderer.setSeriesFillPaint(0, ChartColor.RED);
        renderer.setBaseShapesVisible(true);
        renderer.setDrawOutlines(true);
        renderer.setUseFillPaint(true);
        renderer.setSeriesStroke(0, new BasicStroke(1.0F));
        renderer.setSeriesOutlineStroke(0, new BasicStroke(1.0F));
        final Ellipse2D ellipse2D = new Ellipse2D.Double(-2.0D, -2.0D, 4.0D, 4.0D);
        renderer.setSeriesShape(0, ellipse2D);

        renderer.setSeriesLinesVisible(1, false);
        renderer.setSeriesShapesVisible(1, true);
        renderer.setSeriesPaint(1, ChartColor.ORANGE);
        renderer.setSeriesFillPaint(1, ChartColor.ORANGE);

        renderer.setSeriesStroke(1, new BasicStroke(1.0F));
        renderer.setSeriesOutlineStroke(1, new BasicStroke(1.0F));
        renderer.setSeriesShape(1, ellipse2D);

        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(this.getDigNum());
        nf.setGroupingUsed(false);
        StandardXYToolTipGenerator generator = new StandardXYToolTipGenerator("{0}: ({1}, {2})", NumberFormat.getInstance(), new DecimalFormat(this.getDigNumFormat()));
        renderer.setBaseToolTipGenerator(generator);

        FixedNumberAxis axis = new FixedNumberAxis();
        axis.setNumberFormatOverride(nf);

        List<Double> data = Lists.newArrayList();
        for (int i = 0; i < meanData.length; i++) {
            if (!Double.isNaN(meanData[i])) {
                data.add(meanData[i]);
            }
        }

        for (int j = 0; j < scatterDto.getPointDto().getY().length; j++) {
            if (!Double.isNaN(scatterDto.getPointDto().getY()[j])) {
                data.add(scatterDto.getPointDto().getY()[j]);
            }
        }

        //The maximum and minimum values are calculated to limit the range of Y coordinates.
        double min = 0;
        double max = 0;
        if (data != null && data.size() != 0) {
            min = MathUtils.findMin(data);
            max = MathUtils.findMax(data);
        }

        //Define the range of Y coordinates.
        if (max != min) {
            final double factor = 0.20;
            double reserve = (max - min) * factor;
            axis.setR(new Range(min - reserve, max + reserve));
            axis.setTickLabelInsets(new RectangleInsets(15, 0, 15, 0));
            xyPlot.setRangeAxis(axis);
        }

        FixedNumberAxis domainAxis = new FixedNumberAxis();
        domainAxis.setR(xyPlot.getDomainAxis().getRange());
        xyPlot.setDomainAxis(domainAxis);

        //Draw lcl、-2σ、-σ、c、σ、2σ、ucl line.
        Color[] colors = new Color[]{ChartColor.RED, ChartColor.BLUE, ChartColor.GREEN, ChartColor.GRAYBLUE, ChartColor.GREEN, ChartColor.BLUE, ChartColor.RED};
        String[] lineTitles = new String[]{"lcl=", "-2σ=", "-σ=", "c=", "σ=", "2σ=", "ucl="};
        ValueMarker valuemarker;
        for (int i = 0; i < meanData.length; i++) {
            valuemarker = ChartUtil.drawWarningLines(meanData[i], lineTitles[i] + new DecimalFormat(this.getDigNumFormat()).format(meanData[i]), colors[i]);
            valuemarker.setStroke(new BasicStroke(1.0F));
            xyPlot.addRangeMarker(valuemarker);
        }

        //Draw usl line
        if (this.scatterDto.getStandardDto() != null && this.scatterDto.getStandardDto().getUslValue() != null) {
            double usl = this.scatterDto.getStandardDto().getUslValue();
            valuemarker = ChartUtil.drawWarningLines(usl, "usl=" + new DecimalFormat(this.getDigNumFormat()).format(usl), ChartColor.MAGENTA);
            valuemarker.setStroke(new BasicStroke(1.0F));
            xyPlot.addRangeMarker(valuemarker);
        }

        //Draw lsl line
        if (this.scatterDto.getStandardDto() != null && this.scatterDto.getStandardDto().getLslValue() != null) {
            double lsl = this.scatterDto.getStandardDto().getLslValue();
            valuemarker = ChartUtil.drawWarningLines(lsl, "lsl=" + new DecimalFormat(this.getDigNumFormat()).format(lsl), ChartColor.MAGENTA);
            valuemarker.setStroke(new BasicStroke(1.0F));
            xyPlot.addRangeMarker(valuemarker);
        }

        xyPlot.setRenderer(renderer);

        return localChart;
    }

    /*
    * Create dataset for ScatterChart, and the abnormal data is processed as necessary.
    * */
    private IntervalXYDataset createDataset(double[] scatterPointX, double[] scatterPointY, double[] scatterLineX, double[] scatterLineY) {
        if (scatterPointX == null || scatterPointY == null || scatterLineX == null || scatterLineY == null) {
            isDataValid = false;
        } else {
            XYSeries series1 = new XYSeries("AVG");
            XYSeries series2 = new XYSeries("TestItem Value");
            for (int i = 0; i < scatterLineX.length; i++) {
                if (Double.isNaN(scatterLineX[i]) || Double.isNaN(scatterLineY[i])) {
                    continue;
                }
                series1.add(scatterLineX[i], scatterLineY[i]);
            }
            for (int i = 0; i < scatterPointX.length; i++) {
                if (Double.isNaN(scatterPointX[i]) || Double.isNaN(scatterPointY[i])) {
                    continue;
                }
                series2.add(scatterPointX[i], scatterPointY[i]);
            }
            XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
            xySeriesCollection.addSeries(series1);
            xySeriesCollection.addSeries(series2);
            return xySeriesCollection;
        }
        return null;
    }
}
